package com.umakr.ax.core.base;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.umakr.ax.core.Constants;
import com.umakr.ax.utils.DataUtil;
import com.umakr.ax.utils.ExceptionUtil;
import com.umakr.ax.utils.InstanceUtil;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.IService;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.google.common.collect.Maps;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.annotation.Transactional;

import com.baomidou.mybatisplus.plugins.Page;

/**
 * 业务逻辑层基类<br/>
 * 继承基类后必须配置CacheConfig(cacheNames="")
 * 
 * @author umakr
 * @version 2016年5月20日 下午3:19:19
 */
public abstract class BaseService<B extends BaseMapper<T>,T extends BaseModel> extends ServiceImpl<B,T> implements ApplicationContextAware,IService<T> {
    protected final Log logger = LogFactory.getLog(this.getClass());
    private static final String PAGENUM = "pageNum";
    private static final String PAGESEZE = "pageSize";
    private static final String ORDERBY = "orderBy";
    @Autowired
    protected BaseMapper<T> mapper;
    protected ApplicationContext applicationContext;
    protected B getMapper(){
        //noinspection unchecked
        return (B) mapper;
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }
    /** 分页查询 */
    public  Page<Map<String,Object>> getMapPageable(Map<String, Object> params) {
        Integer current = 1;
        Integer size = 10;
        String orderBy = "id";
        if (DataUtil.isNotEmpty(params.get(PAGENUM))) {
            current = Integer.valueOf(params.get(PAGENUM).toString());
        }
        if (DataUtil.isNotEmpty(params.get(PAGESEZE))) {
            size = Integer.valueOf(params.get(PAGESEZE).toString());
        }
        if (DataUtil.isNotEmpty(params.get(ORDERBY))) {
            orderBy = (String)params.get(ORDERBY);
            params.remove(ORDERBY);
        }
        if (size == -1) {
            return new Page<>();
        }
        Page<Map<String,Object>> page = new Page<>(current, size, orderBy);
        page.setAsc(false);
        return page;
    }
    /** 分页查询 */
    public  Page<T> getEntityPageable(Map<String, Object> params) {
        Integer current = 1;
        Integer size = 10;
        String orderBy = "id";
        if (DataUtil.isNotEmpty(params.get(PAGENUM))) {
            current = Integer.valueOf(params.get(PAGENUM).toString());
        }
        if (DataUtil.isNotEmpty(params.get(PAGESEZE))) {
            size = Integer.valueOf(params.get(PAGESEZE).toString());
        }
        if (DataUtil.isNotEmpty(params.get(ORDERBY))) {
            orderBy = (String)params.get(ORDERBY);
            params.remove(ORDERBY);
        }
        if (size == -1) {
            return new Page<>();
        }
        Page<T> page = new Page<>(current, size, orderBy);
        page.setAsc(false);
        return page;
    }
    /** 分页查询 */
    public static Page<Long> getPage(Map<String, Object> params) {
        Integer current = 1;
        Integer size = 10;
        String orderBy = "id";
        if (DataUtil.isNotEmpty(params.get(PAGENUM))) {
            current = Integer.valueOf(params.get(PAGENUM).toString());
        }
        if (DataUtil.isNotEmpty(params.get(PAGESEZE))) {
            size = Integer.valueOf(params.get(PAGESEZE).toString());
        }
        if (DataUtil.isNotEmpty(params.get(ORDERBY))) {
            orderBy = (String)params.get(ORDERBY);
            params.remove(ORDERBY);
        }
        if (size == -1) {
            return new Page<>();
        }
        Page<Long> page = new Page<>(current, size, orderBy);
        page.setAsc(false);
        return page;
    }

    /** 根据Id查询(默认类型T) */
    public Page<T> getPage(Page<Long> ids) {
        if (ids != null) {
            Page<T> page = new Page<>(ids.getCurrent(), ids.getSize());
            page.setTotal(ids.getTotal());
            List<T> records =  new ArrayList<>();
            for (Long id : ids.getRecords()) {
                records.add(this.queryById(id));
            }
            page.setRecords(records);
            return page;
        }
        return new Page<>();
    }

    /** 根据Id查询(默认类型T) */
    public Page<Map<String, Object>> getPageMap(Page<Long> ids) {
        if (ids != null) {
            Page<Map<String, Object>> page = new Page<>(ids.getCurrent(), ids.getSize());
            page.setTotal(ids.getTotal());
            List<Map<String, Object>> records =  new ArrayList<>();
            for (Long id : ids.getRecords()) {
                records.add(InstanceUtil.transBean2Map(this.queryById(id)));
            }
            page.setRecords(records);
            return page;
        }
        return new Page<>();
    }

    /** 根据Id查询(cls返回类型Class) */
    public <K> Page<K> getPage(Page<Long> ids, Class<K> cls) {
        if (ids != null) {
            Page<K> page = new Page<>(ids.getCurrent(), ids.getSize());
            page.setTotal(ids.getTotal());
            List<K> records = InstanceUtil.newArrayList();
            for (Long id : ids.getRecords()) {
                T t = this.queryById(id);
                K k = InstanceUtil.to(t, cls);
                records.add(k);
            }
            page.setRecords(records);
            return page;
        }
        return new Page<>();
    }

    /** 根据Id查询(默认类型T) */
    public List<T> getList(List<Long> ids) {
        List<T> list = InstanceUtil.newArrayList();
        if (ids != null) {
            for (Long id : ids) {
                list.add(this.queryById(id));
            }
        }
        return list;
    }

    /** 根据Id查询(cls返回类型Class) */
    public <K> List<K> getList(List<Long> ids, Class<K> cls) {
        List<K> list = InstanceUtil.newArrayList();
        if (ids != null) {
            for (Long id : ids) {
                T t = this.queryById(id);
                K k = InstanceUtil.to(t, cls);
                list.add(k);
            }
        }
        return list;
    }

    /**
     * 软删除
     * @param id
     * @param userId
     */
    @Transactional
    public void del(Long id, Long userId) {
        try {
            T record = this.queryById(id);
            mapper.updateById(record);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Transactional
    public void delete(Long id) {
        try {
            mapper.deleteById(id);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    @Transactional
    public T update(T record) {
        try {
            if (record.getId() == null) {
                mapper.insert(record);
            } else {
                T org = queryById(record.getId());
                T update = InstanceUtil.getDiff(org, record);
                update.setId(record.getId());
                mapper.updateById(update);
            }
        } catch (DuplicateKeyException e) {
            String msg = ExceptionUtil.getStackTraceAsString(e);
            logger.error(Constants.EXCEPTION_HEAD + msg, e);
            throw new RuntimeException("已经存在相同的配置.");
        } catch (Exception e) {
            String msg = ExceptionUtil.getStackTraceAsString(e);
            logger.error(Constants.EXCEPTION_HEAD + msg, e);
            throw new RuntimeException(msg);
        }
        return record;
    }

    @Transactional
    @SuppressWarnings("unchecked")
    public T queryById(Long id) {
        try {
            return mapper.selectById(id);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    public Page<T> query(Map<String, Object> params) {
        Page<T> page = getEntityPageable(params);
        Wrapper<T> entityWrapper = ParameterParser.parser(params);
        return selectPage(page,entityWrapper);
    }
    public Page<Map<String, Object>> queryMapPage(Map<String, Object> params) {
        Page<Map<String, Object>> page = getMapPageable(params);
        Wrapper<T> entityWrapper = ParameterParser.parser(params);
        return selectMapsPage(page, entityWrapper);
    }

    public List<T> queryList(Map<String, Object> params) {
        Wrapper<T> entityWrapper = ParameterParser.parser(params);
        return selectList(entityWrapper);
    }


    /** 获取缓存键值 */
    protected String getCacheKey(Object id) {
        String cacheName = getCacheKey();
        return new StringBuilder(Constants.CACHE_NAMESPACE).append(cacheName).append(":").append(id).toString();
    }

    /** 获取缓存键值 */
    protected String getLockKey(Object id) {
        String cacheName = getCacheKey();
        return new StringBuilder(Constants.CACHE_NAMESPACE).append(cacheName).append(":LOCK:").append(id).toString();
    }

    /**
     * @return 键
     */
    private String getCacheKey() {
        Class<?> cls = getClass();
        String cacheName = Constants.CACHEKEYMAP.get(cls);
        if (StringUtils.isBlank(cacheName)) {
            CacheConfig cacheConfig = cls.getAnnotation(CacheConfig.class);
            if (cacheConfig == null || cacheConfig.cacheNames() == null || cacheConfig.cacheNames().length < 1) {
                cacheName = getClass().getName();
            } else {
                cacheName = cacheConfig.cacheNames()[0];
            }
            Constants.CACHEKEYMAP.put(cls, cacheName);
        }
        return cacheName;
    }

    public Map<String,Object> resultMap(int resultCode,String errMsg){
        Map<String,Object> resultMap = Maps.newLinkedHashMap();
        resultMap.put("errorCode",resultCode);
        resultMap.put("msg",errMsg);
        return resultMap;
    }
    public Map<String,Object> resultMap(Object resultMap,int resultCode,String errMsg){
        Map<String,Object> resultMapx = Maps.newLinkedHashMap();
        resultMapx.put("data",resultMap);
        resultMapx.put("errorCode",resultCode);
        resultMapx.put("msg",errMsg);
        return resultMapx;
    }
    public Map<String,Object> resultMap(Object resultMap){
        return resultMap(resultMap,1,"");
    }
}
